package com.suncky.frame.base.adapter;

import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.OnRebindCallback;
import androidx.databinding.ViewDataBinding;
import androidx.recyclerview.widget.RecyclerView;

import com.suncky.frame.base.adapter.holder.RecyclerViewBindingHolder;
import com.suncky.frame.base.adapter.listener.OnItemClickListener;
import com.suncky.frame.base.adapter.listener.OnItemLongClickListener;
import com.suncky.frame.base.adapter.listener.RecyclerAdapterListener;
import com.suncky.frame.base.adapter.listener.RecyclerBindingAdapterListener;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * RecyclerView列表适配器基类。
 * 使用ViewDataBinding进行数据绑定;
 */
public abstract class RecyclerViewBindingAdapter<M,B extends ViewDataBinding> extends RecyclerView.Adapter<RecyclerViewBindingHolder<B>> implements RecyclerViewBindingHolder.SubViewEventListener<B>, Optional<M> {
    protected Context mContext;
    protected List<M> mDatas;
    protected RecyclerAdapterListener mListener;
    protected RecyclerBindingAdapterListener<B> mBindingAdapterListener;
    protected OnItemClickListener itemClickListener;
    protected OnItemLongClickListener itemLongClickListener;
    protected int mMarkedPosition = -1;//标记位置
    private RecyclerView mRecyclerView;
    protected boolean optionEnable = true;//是否启用选择
    private boolean optionCancelable = true;//是否可取消选择
    private int optionType = TYPE_NOME;
    private final List<M> selected = new ArrayList<>();

    private final SparseArray<RecyclerViewBindingHolder<B>> holderArray = new SparseArray<>();//保存ViewHolder和position的对应关系

    public RecyclerViewBindingAdapter(Context context){
        this(context, null);
    }
    public RecyclerViewBindingAdapter(Context context, List<M> data){
        this(context, data, TYPE_NOME);
    }
    public RecyclerViewBindingAdapter(Context context, List<M> data, int optionType){
        this(context, data,optionType,null);
    }

    public RecyclerViewBindingAdapter(Context context, List<M> data, int optionType, RecyclerBindingAdapterListener<B> listener) {
        this.mContext = context;
        this.mDatas = data;
        this.optionType = optionType;
        this.mBindingAdapterListener = listener;
    }

    /**
     * This is used to block items from updating themselves. RecyclerView wants to know when an
     * item is invalidated and it prefers to refresh it via onRebind. It also helps with performance
     * since data binding will not update views that are not changed.
     */
    private final OnRebindCallback<B> mOnRebindCallback = new OnRebindCallback<B>() {
        @Override
        public boolean onPreBind(B binding) {
            if (mRecyclerView == null || mRecyclerView.isComputingLayout()) {
                return true;
            }
            int childAdapterPosition = mRecyclerView.getChildAdapterPosition(binding.getRoot());
            if (childAdapterPosition == RecyclerView.NO_POSITION) {
                return true;
            }
            notifyItemChanged(childAdapterPosition);
            return false;
        }
    };

    @NonNull
    @Override
    public RecyclerViewBindingHolder<B> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        B binding = onCreateDataBinding(LayoutInflater.from(mContext), parent, viewType);
        if (binding == null) {
            binding = onCreateDataBinding(parent, viewType);
        }
//        binding.addOnRebindCallback(mOnRebindCallback);
        return new RecyclerViewBindingHolder<>(binding, this);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerViewBindingHolder<B> holder, int position) {
        convert(holder, getItem(position), position);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerViewBindingHolder<B> holder, int position, @NonNull List<Object> payloads) {
        holderArray.put(position, holder);
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position);
        } else {
            convert(holder, getItem(position), position, payloads);
            holder.getBinding().executePendingBindings();
        }
    }

    /**
     * 创建并返回ViewDataBinding对象
     * @param parent 父view
     * @param viewType {@link #getItemViewType(int)}返回的view类型，用于多种item布局的情况
     * @return ViewDataBinding对象
     */
    public B onCreateDataBinding(ViewGroup parent, int viewType){
        return null;
    };

    /**
     * 创建并返回ViewDataBinding对象
     * @param inflater LayoutInflater对象,用于创建ViewDataBinding对象
     * @param parent 父view
     * @param viewType {@link #getItemViewType(int)}返回的view类型，用于多种item布局的情况
     * @return ViewDataBinding对象
     */
    public abstract B onCreateDataBinding(@NonNull LayoutInflater inflater, ViewGroup parent, int viewType);

    /**
     * 绑定数据(全量绑定)
     * @param holder ViewHolder
     * @param item 数据
     * @param position 位置
     */
    public abstract void convert(RecyclerViewBindingHolder<B> holder, M item, int position);

    /**
     * 绑定数据(可局部绑定)
     * @param holder ViewHolder
     * @param item 数据
     * @param position 位置
     * @param payloads 来自 {@link #notifyItemChanged(int, Object)} 或 {@link #notifyItemRangeChanged(int, int, Object)} 中的payload参数组成的数组。
     *                 如果payloads数组不为空,会自动调用此方法进行数据绑定,可使用payloads中的数据进行局部刷新.
     *                 如果payloads数组为空,会自动调用 {@link #convert(RecyclerViewBindingHolder, Object, int)} 方法进行数据全量绑定
     */
    public void convert(RecyclerViewBindingHolder<B> holder, M item, int position, @NonNull List<Object> payloads){

    }

    @Override
    public int getItemCount() {
        return mDatas != null ? mDatas.size() : 0;
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
    }

    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = null;
    }

    /**
     * 获取标记位置
     * @return
     */
    public int getMarkedPosition() {
        return mMarkedPosition;
    }

    /**
     * 获取标记位置
     * @param position
     */
    public void setMarkedPosition(int position) {
        this.mMarkedPosition = position;
    }

    /**
     * 获取标记位置下的数据
     * @return
     */
    public M getMarkedPositionItem(){
        return getItem(mMarkedPosition);
    }

    /**
     * 刷新标记位置
     * @param position 要标记的位置
     */
    public void refresh(int position) {
        if (mDatas == null || position < 0 || position > mDatas.size() - 1) {
            return;
        }
        if (mMarkedPosition >= 0) {
            notifyItemChanged(mMarkedPosition);
        }
        mMarkedPosition = position;
        notifyItemChanged(position);
    }

    /**
     * 设置数据源
     * @param data
     */
    public void setData(List<M> data) {
        if (mDatas == null || mDatas != data) {
            mDatas = data;
        }
        notifyDataSetChanged();
    }

    /**
     * 设置数据源及标记位置
     * @param data
     */
    public void setData(List<M> data, int position) {
        mDatas = data;
        mMarkedPosition = position;
        notifyDataSetChanged();
    }

    /**
     * 在末尾添加加数据
     * @param data
     */
    public void appendData(List<M> data){
        if (data == null || data.size() == 0) {
            return;
        }
        if (mDatas == null) {
            mDatas = new ArrayList<>();
        }
        int startPosition = mDatas.size();
        mDatas.addAll(data);
        notifyItemRangeInserted(startPosition, data.size());
    }

    /**
     * 在末尾添加加数据
     * @param data
     */
    public void appendData(M data){
        if (data == null) {
            return;
        }
        if (mDatas == null) {
            mDatas = new ArrayList<>();
        }
        int startPosition = mDatas.size();
        mDatas.add(data);
        notifyItemRangeInserted(startPosition, 1);
    }

    /**
     * 插入数据列表
     * @param position 插入位置
     * @param datas 数据列表
     */
    public void insertData(int position, List<M> datas) {
        if (position < 0 || position > getItemCount() || datas == null || datas.size() == 0) {
            return;
        }
        if (mDatas == null) {
            mDatas = new ArrayList<>();
        }
        mDatas.addAll(position, datas);
        notifyItemRangeInserted(position, datas.size());
    }

    /**
     * 插入数据
     * @param position 插入位置
     * @param data 数据
     */
    public void insertData(int position, M data) {
        if (data == null || position < 0 || position > getItemCount()) {
            return;
        }
        if (mDatas == null) {
            mDatas = new ArrayList<>();
        }
        mDatas.add(position, data);
        notifyItemRangeInserted(position, 1);
    }

    /**
     * 移除数据
     * @param position 移除的数据位置
     */
    public void remove(int position) {
        if (mDatas == null || position < 0 || position > mDatas.size() - 1) {
            return;
        }
        mDatas.remove(position);
        notifyItemRemoved(position);
    }

    /**
     * 移除数据,重新绑定数据
     * @param position
     */
    public void removeUpdate(int position) {
        if (mDatas == null || position < 0 || position > mDatas.size() - 1) {
            return;
        }
        mDatas.remove(position);
        notifyDataSetChanged();
    }

    /**
     * 清空数据源
     */
    public void clearData() {
        if (mDatas != null) {
            mDatas.clear();
        }
        notifyDataSetChanged();
    }

    public List<M> getData() {
        return mDatas;
    }

    public M getItem(int position) {
        if (mDatas == null || position < 0 || position > mDatas.size() - 1) {
            return null;
        }
        return mDatas.get(position);
    }

    /**
     * 设置事件监听
     * @deprecated use {@link #setBindingAdapterListener(RecyclerBindingAdapterListener)} instead
     * @param listener
     */
    @Deprecated
    public void setAdapterListener(RecyclerAdapterListener listener) {
        mListener = listener;
    }

    /**
     * 设置事件监听
     * @param mBindingAdapterListener
     */
    public void setBindingAdapterListener(RecyclerBindingAdapterListener<B> mBindingAdapterListener) {
        this.mBindingAdapterListener = mBindingAdapterListener;
    }

    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener itemLongClickListener) {
        this.itemLongClickListener = itemLongClickListener;
    }

    @Override
    public void onClick(View v, int position, B binding) {
        if (v == holderArray.get(position).itemView && itemClickListener != null) {
            itemClickListener.onItemClick(v, position);
            return;
        }
        if (mListener != null) {
            mListener.onClickEvent(position, v.getId());
        }
        if (mBindingAdapterListener != null) {
            mBindingAdapterListener.onClickEvent(position, v.getId(), binding);
        }
    }

    @Override
    public void onLongClick(View v, int position, B binding) {
        if (v == holderArray.get(position).itemView && itemLongClickListener != null) {
            itemLongClickListener.onItemLongClick(v, position);
            return;
        }
        if (mListener != null) {
            mListener.onLongClickEvent(position, v.getId());
        }
        if (mBindingAdapterListener != null) {
            mBindingAdapterListener.onLongClickEvent(position, v.getId(), binding);
        }
    }

    @Override
    public void onItemClick(View v, int position, int subPosition, B binding) {

    }

    /**
     * 强行清除已选项
     */
    public void clearChoice(){
        unSelectAll();
    }

    /**
     * 是否启用选项
     * @return
     */
    public boolean isOptionEnable() {
        return this.optionEnable;
    }

    @Override
    public void selectAll() {
        if (!optionEnable || mDatas == null || mDatas.isEmpty() || optionType != TYPE_MULTI) {
            return;
        }
        selected.clear();
        selected.addAll(mDatas);
        notifyItemRangeChanged(0,getItemCount());
    }

    @Override
    public void unSelectAll() {
        Iterator<M> iterator = selected.iterator();
        M item;
        while (iterator.hasNext()) {
            item = iterator.next();
            iterator.remove();
            notifyItemChanged(mDatas.indexOf(item));
        }
    }

    /**
     * 选项变化,更新选项状态
     * @param position
     */
    public void notifySelectChange(int position) {
        notifySelectChange(position,null,null);
    }

    /**
     * 选项变化,更新选项状态
     * @param position 更新选项位置
     * @param payloadSelect 选择的item局部刷新参数
     * @param payloadUnselect 未选择的选择item局部刷新参数
     */
    public void notifySelectChange(int position, @Nullable Object payloadSelect, @Nullable Object payloadUnselect) {
        notifySelectChange(getItem(position), position,payloadSelect,payloadUnselect);
    }

    /**
     * 选项变化,更新选项状态
     * @param o 更新的选项
     */
    public void notifySelectChange(M o) {
        notifySelectChange(o,null,null);
    }

    /**
     * 选项变化,更新选项状态
     * @param o 更新的选项
     * @param payloadSelect 选择的item局部刷新参数
     * @param payloadUnselect 未选择的选择item局部刷新参数
     */
    public void notifySelectChange(M o, @Nullable Object payloadSelect, @Nullable Object payloadUnselect) {
        notifySelectChange(o, mDatas.indexOf(o), payloadSelect, payloadUnselect);
    }

    private void notifySelectChange(M o, int position, @Nullable Object payloadSelect, @Nullable Object payloadUnselect) {
        if (o == null || !isOptionEnable()) {
            return;
        }
        if (isSelected(o)) {
            if (!optionCancelable) {
                return;
            }
            selected.remove(o);
        } else {
            if (optionType == TYPE_NOME) {
                return;
            }
            if (optionType == TYPE_SINGLE) {
                if (!selected.isEmpty()) {
                    M item= selected.get(0);
                    selected.remove(item);
                    notifyItemChanged(mDatas.indexOf(item), payloadUnselect);
                }
            }
            selected.add(o);
        }
        notifyItemChanged(position, payloadSelect);
    }

    /**
     * 选项变化,更新选项状态
     * @param item
     * @deprecated 此方法会重新绑定数据,为获得更好的体验,请使用 {@link #notifySelectChange(Object)} 方法
     */
    public void select(M item) {
        if (item == null) {
            return;
        }
        switch (OptionType()) {
            case TYPE_NOME:
                return;
            case TYPE_SINGLE:
                if (!isSelected(item)) {
                    selected.clear();
                    selected.add(item);
                    notifyDataSetChanged();
                }
                break;
            case TYPE_MULTI:
                if (!isSelected(item)) {
                    selected.add(item);
                } else {
                    selected.remove(item);
                }
                notifyDataSetChanged();
                break;
        }
    }

    /**
     * 选项变化,更新选项状态
     * @param position
     * @deprecated 此方法会重新绑定数据,为获得更好的体验,请使用 {@link #notifySelectChange(int)} 方法
     */
    public void select(int position) {
        select(getItem(position));
    }

    public void selectOption(int position) {
        selectOption(getItem(position));
    }

    @Override
    public void selectOption(M item) {
        if (item == null || !isOptionEnable() || isSelected(item)) {
            return;
        }
        if (optionType == Optional.TYPE_SINGLE && !selected.isEmpty()) {
            M m = selected.get(0);
            selected.remove(m);
            notifyItemChanged(mDatas.indexOf(m));
        }
        selected.add(item);
        notifyItemChanged(mDatas.indexOf(item));
    }

    public void unselectOption(int position) {
        unselectOption(getItem(position));
    }

    @Override
    public void unselectOption(M item) {
        if (item == null || !isOptionEnable() || !isCancelable()) {
            return;
        }
        if (isSelected(item)) {
            selected.remove(item);
            notifyItemChanged(mDatas.indexOf(item));
        }
    }

    @Override
    public void invertSelect() {
        if (!optionEnable || mDatas == null || mDatas.isEmpty() || optionType != TYPE_MULTI) {
            return;
        }
        for (M m : mDatas) {
            if (isSelected(m)) {
                if (!optionCancelable) {
                    continue;
                }
                selected.remove(m);
            } else {
                selected.add(m);
            }
        }
        notifyItemRangeChanged(0, getItemCount());
    }

    @Override
    public boolean isSelected(M item) {
        if (item == null) {
            return false;
        }
        return selected.contains(item);
    }

    @Override
    public List<M> getSelectedOptions() {
        return selected;
    }

    @Override
    public void setSelectedOptions(List<M> selectedOptions) {
        if (selectedOptions == null || selectedOptions.isEmpty()) {
            clearChoice();
            return;
        }
        for (M m : selectedOptions) {
            if (!isSelected(m)) {
                selected.add(m);
                notifyItemChanged(mDatas.indexOf(m));
            }
        }
        for (M m : selected) {
            if (!selectedOptions.contains(m)) {
                selected.remove(m);
                notifyItemChanged(mDatas.indexOf(m));
            }
        }
    }

    @Override
    public void addSelectedOptions(List<M> options) {
        if (options == null || options.size() == 0) {
            return;
        }
        for (M m : options) {
            if (!this.selected.contains(m)) {
                this.selected.add(m);
                notifyItemChanged(mDatas.indexOf(m));
            }
        }
    }

    @Override
    public int OptionType() {
        return optionType;
    }

    @Override
    public void setOptionType(@OptionalType int optionType) {
        this.optionType = optionType;
    }

    @Override
    public void cancelable(boolean able) {
        this.optionCancelable = able;
    }

    @Override
    public boolean isCancelable() {
        return this.optionCancelable;
    }
}
